package com.csw.shuanfa.CodeImprove.GuavaLimit.tokenLimter;


import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TokenLimiter {

    private ArrayBlockingQueue<String> blockingQueue;

    //容量大小
    private int limit;

    //令牌的产生间隔
    private int period;

    //令牌每次产生的个数
    private int amount;

    public TokenLimiter(int limit, int period, int amount) {
        this.limit = limit;
        this.period = period;
        this.amount = amount;

        blockingQueue = new ArrayBlockingQueue<>(limit);
    }

    /**
     * 先生产2个令牌，减少4个令牌；再每500ms生产2个令牌，减少4个令牌
     */
    public static void main(String[] args) throws InterruptedException {
        int period = 500;
        TokenLimiter limiter = new TokenLimiter(8, period, 2);
        limiter.init();

        Object lock = new Object();
        limiter.start(lock);

        //让线程先产生2个令牌(溢出)
        synchronized (lock) {
            lock.wait();
        }
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 4; j++) {
                String s = i + "，" + j + "：";
                if (limiter.tryAcquire()) {
                    //s + "拿到令牌");
                } else {
                    //s + "拒绝");
                }
            }
            Thread.sleep(period);
        }
    }

    void init() {
        for (int i = 0; i < limit; i++) {
            blockingQueue.add("lp");
        }
    }

    private void addToken(int amount) {
        for (int i = 0; i < amount; i++) {
            //溢出返回false
            blockingQueue.offer("lp");
        }
    }

    /**
     * 获取令牌
     *
     * @return
     */
    public boolean tryAcquire() {
        //队首元素出队
        return blockingQueue.poll() != null ? true : false;
    }

    /**
     * 生产令牌
     */
    void start(Object lock) {
        Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> {
            synchronized (lock) {
                addToken(this.amount);
                lock.notify();
            }
        }, 500, this.period, TimeUnit.MILLISECONDS);
    }

}
